/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "../game.h"


static bool				anim_isClient;

static anim_t *			cl_animationsHashTable[MAX_ANIMATIONS];
static anim_t *			cl_animations[MAX_ANIMATIONS];
static int				cl_numAnimations=0;
#define ANIMATIONS_HASH_SIZE MAX_ANIMATIONS/4

/*
 ==================
 Anim_ParseAnim
 ==================
*/

static bool Anim_ParseAnim (script_t *script, anim_t *anim){

	token_t	token;
	char	*text;
	int		version;
	int i,j;

	// Parse the version
	if (!odParser->ExpectTokenString(script, &token, MD5_VERSION_STRING)){
		odParser->ScriptError(script, "missing 'MD5Version'");
		return false;
	}
	if (!odParser->ReadSignedInt(script, &version)){
		odParser->ScriptError(script, "missing 'MD5Version'");
		return false;
	}
	if (version != MD5_VERSION){
		odParser->ScriptError(script, "wrong version number (%i should be %i)", version, MD5_VERSION);
		return false;
	}

	// Skip the command line
	if (!odParser->ExpectTokenString(script, &token, "commandLine")){
		odParser->ScriptError(script, "missing 'commandLine'");
		return false;
	}
	text = Str_FindChar(script->text, '\"');
	if (text)
		text = Str_FindChar(text+1, '\"');

	if (text)
		script->text = text+1;
	else {
		odParser->ScriptError(script, "missing 'commandLine' parameters");
		return false;
	}

	// Parse the number of frames
	if (!odParser->ExpectTokenString(script, &token, "numFrames")){
		odParser->ScriptError(script, "missing 'numFrames'");
		return false;
	}
	if (!odParser->ReadSignedInt(script, &anim->numFrames)){
		odParser->ScriptError(script, "missing 'numFrames' parameter");
		return false;
	}
	if (anim->numFrames < 1 || anim->numFrames > MD5_MAX_FRAMES){
		odParser->ScriptError(script, "invalid number of frames (%i)", anim->numFrames);
		return false;
	}

	// Parse the number of joints
	if (!odParser->ExpectTokenString(script, &token, "numJoints")){
		odParser->ScriptError(script, "missing 'numJoints'");
		return false;
	}

	if (!odParser->ReadSignedInt(script, &anim->numJoints)){
		odParser->ScriptError(script, "missing 'numJoints'");
		return false;
	}
	if (anim->numJoints < 1 || anim->numJoints > MD5_MAX_JOINTS){
		odParser->ScriptError(script, "invalid number of joints (%i)", anim->numJoints);
		return false;
	}

	// Parse the frame rate
	if (!odParser->ExpectTokenString(script, &token, "frameRate")){
		odParser->ScriptError(script, "missing 'frameRate'");
		return false;
	}
	if(!odParser->ReadSignedInt(script,&anim->frameRate)){
		odParser->ScriptError(script, "missing 'frameRate'");
		return false;
	}
	if(anim->frameRate<1||anim->frameRate>30){
		odParser->ScriptError(script,"invalid frameRate (%i)",anim->frameRate);
		return false;
	}
	
	//parse animation components
	if (!odParser->ExpectTokenString(script, &token, "numAnimatedComponents")){
		odParser->ScriptError(script, "missing 'numAnimatedComponents'");
		return false;
	}
	if(!odParser->ReadSignedInt(script,&anim->numAnimatedComponents)){
		odParser->ScriptError(script, "missing 'numAnimatedComponents'");
		return false;
	}	
	anim->joints = (animJoint_t*)odMemory->Alloc(anim->numJoints * sizeof(animJoint_t), (anim_isClient) ? TAG_CLIENT : TAG_SERVER);
	anim->frames = (animFrame_t*)odMemory->Alloc(anim->numFrames * sizeof(animFrame_t), (anim_isClient) ? TAG_CLIENT : TAG_SERVER);
	for(i=0;i<anim->numFrames;i++)
		anim->frames[i].components = 
			(float*)odMemory->Alloc(anim->numAnimatedComponents * sizeof(float), (anim_isClient) ? TAG_CLIENT : TAG_SERVER);

	//parse hierarchi
	// Skip 'hierarchy'
	if (!odParser->ExpectTokenString(script, &token, "hierarchy")){
		odParser->ScriptError(script, "missing 'hierarchy' ");
		return false;
	}

	// Skip the opening brace
	if (!odParser->ExpectTokenString(script, &token, "{")){
		odParser->ScriptError(script, "missing 'joint' parameters ");
		return false;
	}

	// Parse the hierarchy
	
	for (i = 0; i < anim->numJoints; i++){
		// Parse the name
		if (!odParser->ReadToken(script, &token)){
			odParser->ScriptError(script, "missing 'hierarchy' parameters ");
			return false;
		}

		Str_Copy(anim->joints[i].name, token.string, sizeof(anim->joints[i].name));

		// Parse the parent index
		if ((!odParser->ReadSignedInt(script, &anim->joints[i].parent))||
			(!odParser->ReadSignedInt(script, &anim->joints[i].componentsBits))||
			(!odParser->ReadSignedInt(script, &anim->joints[i].componentsOffset))){
			odParser->ScriptError(script, "missing 'hierarchy' parameters ");
			return false;
		}
	}
	// Skip the closing brace
	if (!odParser->ExpectTokenString(script, &token, "}")){
		odParser->ScriptError(script, "missing '}' after 'hierarchy'");
		return false;
	}

	//parse the bounds
	// Skip 'bounds'
	if (!odParser->ExpectTokenString(script, &token, "bounds")){
		odParser->ScriptError(script, "missing 'bounds' ");
		return false;
	}

	// Skip the opening brace
	if (!odParser->ExpectTokenString(script, &token, "{")){
		odParser->ScriptError(script, "missing '{' after 'bounds'");
		return false;
	}

	for(i=0;i<anim->numFrames;i++){
		if (!odParser->ExpectTokenString(script, &token, "(")){
			odParser->ScriptError(script, "missing '(' ");
			return false;
		}
		//parse bounds[0]
		if ((!odParser->ReadFloat(script, &anim->frames[i].bounds[0][0]))||
			(!odParser->ReadFloat(script, &anim->frames[i].bounds[0][1]))||
			(!odParser->ReadFloat(script, &anim->frames[i].bounds[0][2]))){
			odParser->ScriptError(script, "missing 'bounds[0]' in 'bounds'");
			return false;
		}
		//braces
		if (!odParser->ExpectTokenString(script, &token, ")")){
			odParser->ScriptError(script, "missing ')' ");
			return false;
		}
		if (!odParser->ExpectTokenString(script, &token, "(")){
			odParser->ScriptError(script, "missing '(' ");
			return false;
		}
		//parse bounds[1]
		if ((!odParser->ReadFloat(script, &anim->frames[i].bounds[1][0]))||
			(!odParser->ReadFloat(script, &anim->frames[i].bounds[1][1]))||
			(!odParser->ReadFloat(script, &anim->frames[i].bounds[1][2]))){
			odParser->ScriptError(script, "missing 'bounds[1]' in 'bounds'");
			return false;
		}
		//closing brace
		if (!odParser->ExpectTokenString(script, &token, ")")){
			odParser->ScriptError(script, "missing ')' ");
			return false;
		}
	}
	// Skip the closing brace
	if (!odParser->ExpectTokenString(script, &token, "}")){
		odParser->ScriptError(script, "missing '}'");
		return false;
	}
	//parse the baseFrame
	// Skip 'baseFrame'
	if (!odParser->ExpectTokenString(script, &token, "baseframe")){
		odParser->ScriptError(script, "missing 'baseframe' ");
		return false;
	}

	// Skip the opening brace
	if (!odParser->ExpectTokenString(script, &token, "{")){
		odParser->ScriptError(script, "missing '{' after 'baseframe'");
		return false;
	}

	for(i=0;i<anim->numJoints;i++){
		if (!odParser->ExpectTokenString(script, &token, "(")){
			odParser->ScriptError(script, "missing '(' ");
			return false;
		}
		//parse origin
		if ((!odParser->ReadFloat(script, &anim->joints[i].baseOrigin[0]))||
			(!odParser->ReadFloat(script, &anim->joints[i].baseOrigin[1]))||
			(!odParser->ReadFloat(script, &anim->joints[i].baseOrigin[2]))){
			odParser->ScriptError(script, "missing 'origin' parameters");
			return false;
		}
		//braces
		if (!odParser->ExpectTokenString(script, &token, ")")){
			odParser->ScriptError(script, "missing ')' ");
			return false;
		}
		if (!odParser->ExpectTokenString(script, &token, "(")){
			odParser->ScriptError(script, "missing '(' ");
			return false;
		}
		//parse quat
		if ((!odParser->ReadFloat(script, &anim->joints[i].baseQuat[0]))||
			(!odParser->ReadFloat(script, &anim->joints[i].baseQuat[1]))||
			(!odParser->ReadFloat(script, &anim->joints[i].baseQuat[2]))){
			odParser->ScriptError(script, "missing 'quat' parameters");
			return false;
		}
		anim->joints[i].baseQuat.CalcW();
		
		//closing brace
		if (!odParser->ExpectTokenString(script, &token, ")")){
			odParser->ScriptError(script, "missing ')' ");
			return false;
		}
	}
	// Skip the closing brace
	if (!odParser->ExpectTokenString(script, &token, "}")){
		odParser->ScriptError(script, "missing '}' after 'baseframe'");
		return false;
	}
	
	//parse frames 0,1,2,3...
	for(i=0;i<anim->numFrames;i++){
		int fnum;
		// Skip 'frame'
		if (!odParser->ExpectTokenString(script, &token, "frame")){
			odParser->ScriptError(script, "missing 'frame' ");
			return false;
		}
		//read frame number
		if (!odParser->ReadSignedInt(script, &fnum)){
			odParser->ScriptError(script, "missing frame number");
			return false;
		}
		if(fnum<0||fnum>anim->numFrames){
			odParser->ScriptError(script, "Frame number overflow '%d'",fnum);
			return false;
		}

		// Skip the opening brace
		if (!odParser->ExpectTokenString(script, &token, "{")){
			odParser->ScriptError(script, "missing '{' after 'frame'");
			return false;
		}

		for(j=0;j<anim->numAnimatedComponents;j++){
			if (!odParser->ReadFloat(script, &anim->frames[fnum].components[j])){
				odParser->ScriptError(script, "missing component #%d",j);
				return false;
			}
		}
		if(j!=anim->numAnimatedComponents){
			odParser->ScriptError(script, "Invalid number of animated components (%d) found for frame %d",j,fnum);
			return false;
		}

		// Skip the closing brace
		if (!odParser->ExpectTokenString(script, &token, "}")){
			odParser->ScriptError(script, "missing '}' after 'frame'");
			return false;
		}
	}
	return true;
}

/*
 ==================
 Anim_LoadAnim
 ==================
*/
bool Anim_LoadAnim (const char *name, anim_t *anim){

	script_t	*script;

	// Load the file
	script = odParser->LoadScriptFile(name);
	if (!script){
		odSystem->Printf(S_COLOR_RED "Anim_LoadAnim: '%s' - file not found!\n",name);
		return false;
	}
	odParser->SetScriptFlags(script, SF_NOFATALERRORS | SF_ALLOWPATHNAMES);

	// Parse it
	if (!Anim_ParseAnim(script, anim)){
		odParser->FreeScript(script);
		odSystem->Printf(S_COLOR_RED "Anim_LoadAnim: '%s' - error parsing script!\n",name);
		return false;
	}

	odParser->FreeScript(script);

	Str_Copy(anim->name,name,sizeof(anim->name));

	return true;
}

/*
 ==================
 Anim_ClearAnims
 ==================
*/
void Anim_ClearAnims (void){

}




void Anim_AnimBuildSkeleton(anim_t *anim,skeleton_t *out,float time,bool loop){
	int i;
	int componentsApplied;
	animFrame_t *oldFrame,*newFrame;
	odVec3			newOrigin, oldOrigin, lerpedOrigin;
	odQuat			newQuat, oldQuat, lerpedQuat;
	animJoint_t *joint;
	bool error=false;
	int startFrame=0,endFrame=0;
	float frac=0,frame;
	
	if(!out)return;

	memset(out,0,sizeof(skeleton_t));

	if(!anim)
		return;

	frame=(time)*anim->frameRate*0.001;
	startFrame=(int)frame;
	frac=frame-startFrame;

	startFrame=startFrame%anim->numFrames;
	endFrame=(startFrame+1)%anim->numFrames;

	if(!loop){
		if(frame>=anim->numFrames-1)
			startFrame=endFrame=anim->numFrames-1;
	}

	// compute frame pointers
	oldFrame = &anim->frames[startFrame];
	newFrame = &anim->frames[endFrame];

	//FIX: fix animations so that we can use frame bounds
	out->bounds.Zero();

	//go through all the joints and set values
	for(i = 0, joint = anim->joints; i < anim->numJoints; i++, joint++){
		// set baseframe values
		newOrigin = joint->baseOrigin;
		oldOrigin = joint->baseOrigin;
		
		newQuat = joint->baseQuat;
		oldQuat = joint->baseQuat;
		
		componentsApplied = 0;
		
		// update tranlation bits
		if(joint->componentsBits & 1)
		{
			oldOrigin[0] = oldFrame->components[joint->componentsOffset + componentsApplied];
			newOrigin[0] = newFrame->components[joint->componentsOffset + componentsApplied];
			componentsApplied++;
		}
		
		if(joint->componentsBits & 2)
		{
			oldOrigin[1] = oldFrame->components[joint->componentsOffset + componentsApplied];
			newOrigin[1] = newFrame->components[joint->componentsOffset + componentsApplied];
			componentsApplied++;
		}
		
		if(joint->componentsBits & 4)
		{
			oldOrigin[2] = oldFrame->components[joint->componentsOffset + componentsApplied];
			newOrigin[2] = newFrame->components[joint->componentsOffset + componentsApplied];
			componentsApplied++;
		}
		
		// update quaternion rotation bits
		if(joint->componentsBits & 8)
		{
			oldQuat[0] = oldFrame->components[joint->componentsOffset + componentsApplied];
			newQuat[0] = newFrame->components[joint->componentsOffset + componentsApplied];
			componentsApplied++;
		}
		
		if(joint->componentsBits & 16)
		{
			oldQuat[1] = oldFrame->components[joint->componentsOffset + componentsApplied];
			newQuat[1] = newFrame->components[joint->componentsOffset + componentsApplied];
			componentsApplied++;
		}
		
		if(joint->componentsBits & 32)
		{
			oldQuat[2] = oldFrame->components[joint->componentsOffset + componentsApplied];
			newQuat[2] = newFrame->components[joint->componentsOffset + componentsApplied];
		}
		oldQuat.CalcW();
		newQuat.CalcW();

		lerpedOrigin.Lerp(oldOrigin, newOrigin, frac);
		lerpedQuat.SLerp(oldQuat, newQuat, frac);

		out->joints[i].rotation = lerpedQuat.ToMat3();
		out->joints[i].translation = lerpedOrigin;
		out->parents[i]=joint->parent;
		/*
		if(joint->parent< 0){
			//set the mesh joint orientation
			Quat_ToMat3(lerpedQuat,out->joints[i].axis);
			Vec3_Copy(lerpedOrigin,out->joints[i].origin);
		}
		else{
			vec3_t vtmp;
			mat3_t mtmp;

			Mat3_TransformVector(out->joints[joint->parent].axis,lerpedOrigin,vtmp);
			Vec3_Add(vtmp,out->joints[joint->parent].origin,out->joints[i].origin);

			Quat_ToMat3(lerpedQuat,mtmp);
			Mat3_Multiply(out->joints[joint->parent].axis,mtmp,out->joints[i].axis);
		}
		*/
	}
	out->numJoints=anim->numJoints;
}
void Anim_AnimTransformSkeleton(skeleton_t *skel){
	int c;
	if(!skel)
		return;
	//NOTE: 0 is allways origin with no parent
	for(c=1;c<skel->numJoints;c++){
		odVec3	vtmp;
		odMat3	mtmp;
		odJointMat	*joint=&skel->joints[c];
		int parent=skel->parents[c];

		skel->joints[parent].rotation.ProjectVector(joint->translation,vtmp);
		joint->translation = vtmp + skel->joints[parent].translation;

		mtmp = skel->joints[parent].rotation*joint->rotation;
		joint->rotation = mtmp;

		//update bounds
//		Vec3_Min(joint->translation,skel->bounds[0],skel->bounds[0]);
//		Vec3_Max(joint->translation,skel->bounds[1],skel->bounds[1]);
		//flip y/z
		{
			//swap y/z
			float tmp=skel->bounds[0][1];
			skel->bounds[0][1]=skel->bounds[0][2];
			skel->bounds[0][2]=tmp;
			tmp=skel->bounds[1][1];
			skel->bounds[1][1]=skel->bounds[1][2];
			skel->bounds[1][2]=tmp;
		}
	}
}
void Anim_AnimBlendSkeletons(const skeleton_t *skel1,const skeleton_t *skel2,
						   skeleton_t *res,float frac){
	int c;
	skeleton_t result;

	if(!skel1||!skel2||!res)
		return;
	if(skel1->numJoints!=skel2->numJoints)
		return;
	
	memset(&result,0,sizeof(skeleton_t));

	if(frac<0)frac=0;
	if(frac>1)frac=1;

	for(c=0;c<skel1->numJoints;c++){
		odQuat	q1,q2,q3;

		//rotation
		q1 = skel1->joints[c].rotation.ToQuat();
		q2 = skel2->joints[c].rotation.ToQuat();
		q3.SLerp(q1,q2,frac);
		result.joints[c].rotation = q3.ToMat3();

		//position
		result.joints[c].translation.Lerp(skel1->joints[c].translation,skel2->joints[c].translation,frac);
	}
	result.numJoints=skel1->numJoints;
	*res=result;
}
int Anim_AnimJointIndex(const anim_t *a,const char *name){
	int c;
	for(c=0;c<a->numJoints;c++){
		if(!Str_ICompare(name,a->joints[c].name))
			return c;
	}
	return 0;
}
anim_t *Anim_RegisterAnimation(const char *anim){
	//FIX: Free this in game.media.animations
	anim_t *ret=(anim_t*)odMemory->ClearedAlloc(sizeof(anim_t), (anim_isClient) ? TAG_CLIENT : TAG_SERVER);
	if(!Anim_LoadAnim(anim,ret)){
		odMemory->Free(ret);
		return 0;
	}
	return ret;
}

void Anim_Init (bool isClient){

	anim_isClient = isClient;
}
